
// HideWindowDlg.cpp : implementation file
//

#include "stdafx.h"
#include "HideWindow.h"
#include "HideWindowDlg.h"
#include "afxdialogex.h"
#include <algorithm>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//Hotkey type
static const UINT TOGGLEHIDESHOW_IDENTIFIER = 0;
static const UINT SHOWALL_IDENTIFIER = 1;

#define WM_SHOWNOTIFY WM_USER + 10
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CHideWindowDlg dialog



CHideWindowDlg::CHideWindowDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CHideWindowDlg::IDD, pParent)
	, m_nTransparency(80)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CHideWindowDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CHideWindowDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_DESTROY()
	ON_WM_HOTKEY()
	ON_WM_HSCROLL()
	ON_MESSAGE(WM_SHOWNOTIFY, OnShowNotify)
	ON_WM_SIZE()
END_MESSAGE_MAP()


// CHideWindowDlg message handlers

BOOL CHideWindowDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	ZeroMemory(&m_NotifyIconData, sizeof(NOTIFYICONDATAW));
	m_NotifyIconData.cbSize = sizeof(NOTIFYICONDATAW);
	m_NotifyIconData.hWnd = this->GetSafeHwnd();
	m_NotifyIconData.uID = IDR_MAINFRAME;
	m_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO | NIF_SHOWTIP;
	m_NotifyIconData.uCallbackMessage = WM_SHOWNOTIFY;
	m_NotifyIconData.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
	
	CString str;
	GetWindowText(str);
	_tcscpy_s(m_NotifyIconData.szTip, str.GetBuffer());


	_tcscpy_s(m_NotifyIconData.szInfo, _T("Show or hide foreground window"));
	m_NotifyIconData.uVersion = NOTIFYICON_VERSION;

	_tcscpy_s(m_NotifyIconData.szInfoTitle, str.GetBuffer());

	m_NotifyIconData.dwInfoFlags = NIIF_USER;
	//m_NotifyIconData.hBalloonIcon = m_NotifyIconData.hIcon;

	Shell_NotifyIcon(NIM_ADD, &m_NotifyIconData);

	LoadSettings();

	//Hotkeys
	CListCtrl* pListHotKeys = (CListCtrl*)GetDlgItem(IDC_LIST_HOTKEYS);
	pListHotKeys->SetExtendedStyle(pListHotKeys->GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
	pListHotKeys->InsertColumn(0, _T("Function"), 0, 100);
	pListHotKeys->InsertColumn(1, _T("Hot Key"), LVCFMT_CENTER, 100);
	pListHotKeys->InsertColumn(2, _T("Setting"), LVCFMT_CENTER, 50);
	auto InsertItem = [&](LPCTSTR _title, LPCTSTR _key, LPCTSTR _setting){
		int nItem = pListHotKeys->InsertItem(pListHotKeys->GetItemCount(), _title);
		pListHotKeys->SetItemText(nItem, 1, _key);
		pListHotKeys->SetItemText(nItem, 2, _setting);
	};
	InsertItem(_T("Toggle Hide/Show"), GetHotKeyString(m_vHotKeys[0]), _T("..."));
	InsertItem(_T("Show All"), GetHotKeyString(m_vHotKeys[1]), _T("..."));


	m_wndPreviewCtrl.SubclassDlgItem(IDC_STATIC_PREVIEW, this);
	m_wndPreviewCtrl.SetBitmap(IDB_BITMAP_PREVIEW);
	
	CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_TRANSPARENCY);
	pSlider->SetRange(0, 100);
	pSlider->SetPos(m_nTransparency);
	OnSlideTranparency();

	SetHotKey();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CHideWindowDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CHideWindowDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CHideWindowDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

BOOL CHideWindowDlg::SetHotKey()
{
	int res = 0;
	int num = (int)m_vHotKeys.size();
	unsigned short mod;
	int kn;
	for (int i = 0; i < num; ++i)
	{
		mod = 0;
		kn = (int)m_vHotKeys[i].size();
		for (int k = 0; k < kn - 1; ++k)
		{
			mod |= m_vHotKeys[i][k];
		}
		if (!RegisterHotKey(GetSafeHwnd(), i, mod, m_vHotKeys[i][kn - 1]))
		{
			--res;
		}
	}
	return res == 0;
}

BOOL CHideWindowDlg::UnSetHotKey()
{
	int num = (int)m_vHotKeys.size();
	for (int i = 0; i < num; ++i)
	{
		UnregisterHotKey(GetSafeHwnd(), i);
	}
	return TRUE;
}



void CHideWindowDlg::OnDestroy()
{
	Shell_NotifyIcon(NIM_DELETE, &m_NotifyIconData);
	RestoreWindows();
	UnSetHotKey();
	CDialogEx::OnDestroy();
}


void CHideWindowDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2)
{
	if (nHotKeyId == TOGGLEHIDESHOW_IDENTIFIER)
	{
		CWnd* pWnd = GetForegroundWindow();
		if (pWnd)
		{
			HideActiveWindow(pWnd->GetSafeHwnd());
		}
	}
	else if (nHotKeyId == SHOWALL_IDENTIFIER)
	{
		RestoreWindows();
	}
	CDialogEx::OnHotKey(nHotKeyId, nKey1, nKey2);
}

void CHideWindowDlg::HideActiveWindow(HWND _hWnd)
{
	if (m_mapWindowAttribute.find(_hWnd) == m_mapWindowAttribute.end())
	{
		WindowAttribute attr;
		attr.exStyle = GetWindowLong(_hWnd, GWL_EXSTYLE);
		::GetLayeredWindowAttributes(_hWnd, &attr.crKey,
			&attr.bAlpha, &attr.dwFlags);
		m_mapWindowAttribute[_hWnd] = attr;

		::SetWindowLong(_hWnd, GWL_EXSTYLE, GetWindowLong(_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
		::SetLayeredWindowAttributes(_hWnd, 0, GetAlpha(), LWA_ALPHA);
	}
	else
	{
		::SetWindowLong(_hWnd, GWL_EXSTYLE, m_mapWindowAttribute[_hWnd].exStyle);
		::SetLayeredWindowAttributes(_hWnd, m_mapWindowAttribute[_hWnd].crKey, 
			m_mapWindowAttribute[_hWnd].bAlpha, 
			m_mapWindowAttribute[_hWnd].dwFlags);
		m_mapWindowAttribute.erase(m_mapWindowAttribute.find(_hWnd));
	}
	::RedrawWindow(_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
}

void CHideWindowDlg::RestoreWindows()
{
	for_each(m_mapWindowAttribute.begin(),
		m_mapWindowAttribute.end(),
		[](pair<HWND, WindowAttribute> _it){
		if (::IsWindow(_it.first))
		{
			::SetWindowLong(_it.first, GWL_EXSTYLE, _it.second.exStyle);
			::SetLayeredWindowAttributes(_it.first, _it.second.crKey,
				_it.second.bAlpha, _it.second.dwFlags);
		}
	});
	m_mapWindowAttribute.clear();
}




void CHideWindowDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (pScrollBar->GetSafeHwnd() == GetDlgItem(IDC_SLIDER_TRANSPARENCY)->GetSafeHwnd())
	{
		switch (nSBCode)
		{
		case SB_ENDSCROLL:
			break;
		default:
			m_nTransparency = ((CSliderCtrl*)pScrollBar)->GetPos();
			OnSlideTranparency();
			break;
		}
	}
	CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CHideWindowDlg::OnSlideTranparency()
{
	m_wndPreviewCtrl.SetAlpha(GetAlpha());
}

int CHideWindowDlg::GetAlpha()
{
	return (int)((100 - m_nTransparency) / 100.0f * 255 + 0.5f);
}

LRESULT CHideWindowDlg::OnShowNotify(WPARAM _w, LPARAM _l)
{
	if (_w != IDR_MAINFRAME)
	{
		return 1;
	}
	switch (_l)
	{
	case WM_RBUTTONUP:
		break;
	case WM_LBUTTONUP:
		if (!IsWindowVisible())
		{
			ShowWindow(SW_SHOWNORMAL);
			SetForegroundWindow();
		}
		break;
	default:
		break;
	}
	return 0;
}


void CHideWindowDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	if (nType == SIZE_MINIMIZED)
	{
		ShowWindow(SW_HIDE);
	}
}

void CHideWindowDlg::LoadSettings()
{
	ASSERT(m_vHotKeys.empty());
	vector<UINT> keys;
	keys.push_back(MOD_CONTROL);
	keys.push_back('Q');
	m_vHotKeys.push_back(keys);

	keys[0] = MOD_CONTROL;
	keys[1] = 'W';
	m_vHotKeys.push_back(keys);
}

CString CHideWindowDlg::GetHotKeyString(vector<UINT>& _keys)
{
	CString str = _T("");
	CString strVK = _T("");
	int num = (int)_keys.size();
	for (int i = 0; i < num; ++i)
	{
		if (IsModKey(_keys[i]))
		{
			str += GetKeyString(_keys[i]);
			str += _T("+");
		}
		else
		{
			strVK += GetKeyString(_keys[i]);
			strVK += _T(" ");
		}
	}
	str += strVK;
	str.TrimRight();
	return str;
}

UINT CHideWindowDlg::GetHotKeyMod(vector<UINT>& _keys)
{
	return _keys[0];
}

UINT CHideWindowDlg::GetHotKeyVkey(vector<UINT>& _keys)
{
	return _keys[1];
}

BOOL CHideWindowDlg::IsModKey(UINT _key)
{
	switch (_key)
	{
	case MOD_WIN:
	case MOD_CONTROL:
	case MOD_SHIFT:
		return TRUE;
	default:
		break;
	}
	return FALSE;
}

CString CHideWindowDlg::GetKeyString(UINT _key)
{
	CString str;
	switch (_key)
	{
	case MOD_WIN:
		str = _T("Win");
		break;
	case VK_ESCAPE:
		str = _T("Esc");
		break;
	case MOD_CONTROL:
	case VK_CONTROL:
		str = _T("Ctrl");
		break;
	default:
		str.AppendChar(_key);
		str.MakeUpper();
		break;
	}
	return str;
}
